Simulate quasi 3D groundwater flux and river-groundwater interaction
!! Simulate quasi 3D groundwater flux and river-groundwater interaction !|author: <a href="mailto:giovanni.ravazzani@polimi.it">Giovanni Ravazzani</a> ! license: <a href="http://www.gnu.org/licenses/">GPL</a> ! !### History ! ! current version 1.1 - 12th February 2023 ! ! | version | date | comment | ! |----------|-------------|----------| ! | 1.0 | 14/Sep/2011 | Original code | ! | 1.0 | 22/Feb/2023 | Code rewritten | ! !### License ! license: GNU GPL <http://www.gnu.org/licenses/> ! !### Module Description !Simulate quasi 3D groundwater flux and river-groundwater interaction ! !### References ! ! Ravazzani, G., Rametta, D., & Mancini, M.. (2011). Macroscopic cellular ! automata for groundwater modelling: a first approach. Environmental ! modelling & software, 26(5), 634–643. ! ! Ravazzani, G., Curti, D., Gattinoni, P., Della Valentina, S., ! Fiorucci, A., & Rosso, R.. (2016). Assessing groundwater ! contribution to streamflow of a large alpine river with heat ! tracer methods and hydrological modelling. River research ! and applications, 32(5), 871-884. ! ! Ravazzani, G., Barbero, S., Salandin, A., Senatore, A., ! & Mancini, M.. (2015). An integrated hydrological model ! for assessing climate change impacts on water resources ! of the upper po river basin. Water resources management, ! 29 (4), 1193-1215. ! MODULE Groundwater ! Modules used: USE DataTypeSizes, ONLY : & ! Imported Type Definitions: short, float USE DomainProperties, ONLY : & !imported variables: mask USE GridLib, ONLY : & !imported Type definitions: grid_real, grid_integer, & !imported routines: NewGrid, GridDestroy, & !Imported parameters: NET_CDF USE GridOperations, ONLY : & !Imported routines: GridByIni, CellArea, & !Imported operators: ASSIGNMENT (=) USE IniLib, ONLY : & !Imported definitions: IniList, & !Imported routines: IniOpen, IniClose, & SectionIsPresent, KeyIsPresent, & SubSectionIsPresent, & IniReadReal, IniReadInt USE LogLib, ONLY: & !Imported routines: Catch USE StringManipulation, ONLY : & !Imported routines ToString USE Chronos, ONLY : & !Imported definitions: DateTime , & !Imported variables: timeString, & !Imported operations: OPERATOR (+), & OPERATOR (==), & ASSIGNMENT (=) USE ObservationalNetworks, ONLY : & !Imported routines: ReadMetadata, CopyNetwork, & WriteMetadata, DestroyNetwork, & AssignDataFromGrid, WriteData, & !Imported defined variable: ObservationalNetwork USE Utilities, ONLY : & !Imported routines: GetUnit USE GridStatistics, ONLY : & !imported routines: GetMean USE TableLib, ONLY: & !imported definitions: Table, & !Imported routines: TableNew, TableGetValue IMPLICIT NONE ! Global (i.e. public) Declarations: ! Global TYPE Definitions: TYPE Aquifer TYPE(grid_real) :: top TYPE(grid_real) :: bottom TYPE(grid_real) :: Ks !hydraulic conductivity (m/s) TYPE(grid_real) :: sy !specific yield TYPE(grid_real) :: KsAquitard !hydraulic conductivity of the aquitard TYPE(grid_integer):: domainBC !set domain and boundary conditions type TYPE(grid_real) :: valueBC ! value to be used for boundary condition TYPE(grid_real) :: head0 !head at time t-1 TYPE(grid_real) :: head1 !head at time t END TYPE Aquifer TYPE GroundwaterBasin INTEGER (KIND = short) :: nAquifers !!number of aquifers in the basin TYPE (Aquifer), POINTER :: aquifer (:) END TYPE GroundwaterBasin !Global routines: PUBLIC :: GroundwaterInit PUBLIC :: GroundwaterUpdate PUBLIC :: GroundwaterPointInit PUBLIC :: GroundwaterOutputInit PUBLIC :: GroundwaterOutput PUBLIC :: GroundwaterRiverUpdate !Global variables: TYPE (GroundwaterBasin) :: basin INTEGER (KIND = short) :: dtGroundwater TYPE (grid_real) :: vadoseDepth TYPE (grid_real) :: groundwaterToRiver !!discharge from groundwater to river (m3/s) TYPE (grid_real) :: riverToGroundwater !!discharge from river to groundwater (m3/s) LOGICAL :: riverGroundwaterInteract !Local (private) parameters: INTEGER (KIND = short), PARAMETER, PRIVATE :: BC_DIRICHLET = 2 ! head boundary condition INTEGER (KIND = short), PARAMETER, PRIVATE :: BC_NEUMANN = 3 ! flux boundary condition INTEGER (KIND = short), PARAMETER, PRIVATE :: ACTIVE_CELL = 1 ! active cell within the groundwater domain !local variables TYPE (grid_real), PRIVATE :: transmissivity !!local transmissivity (m2/s) TYPE (grid_real), PRIVATE :: fluxUpward !!flux from lower aquitard (m/s) TYPE (grid_real), PRIVATE :: fluxDownward !!flux from upper aquitard (m/s) TYPE (grid_real), PRIVATE :: riverDem !!reference digital elevation model to compute !!river water surface elevation (m asl) TYPE (grid_real), PRIVATE :: streambedThickness !! streambed thickness (m) TYPE (grid_real), PRIVATE :: streambedConductivity !! streambed conductivity (m/s) TYPE (grid_integer), PRIVATE :: riverGroundwaterID !!id of river reaches that interact with groundwater TYPE (Table), PRIVATE :: riverGroundwaterParameters TYPE (ObservationalNetwork),PRIVATE :: sites INTEGER (KIND = short), ALLOCATABLE, PRIVATE :: fileUnitPointGW (:) INTEGER (KIND = short), PRIVATE :: fileUnitOutGW TYPE (DateTime), PRIVATE :: timePointExport REAL (KIND = float), PRIVATE :: volumeRecharge !! recharge volume (m3) REAL (KIND = float), PRIVATE :: volumeResidual !! balance error (m3) REAL (KIND = float), PRIVATE :: volumeNeumann !!volume from Neumann boundary condition (m3) REAL (KIND = float), PRIVATE :: volumeDirichlet !!volume from Dirichlet boundary condition (m3) REAL (KIND = float), PRIVATE :: volumeChange !! change of stored water volume in groundwater (m3) REAL (KIND = float), PRIVATE :: volumeRiverToGroundwater !! volume from river to groundwater (m3) REAL (KIND = float), PRIVATE :: volumeGroundwaterToRiver !! volume from groundwater to river (m3) !local (private) routines: PRIVATE :: GroundwaterParameterUpdate PRIVATE :: GroundwaterPointExport PRIVATE :: GroundwaterRiverInit !======= CONTAINS !======= ! Define procedures contained in this module. !============================================================================== !| Description: ! Initialize groundwater SUBROUTINE GroundwaterInit & ! (inifile) IMPLICIT NONE !Arguments with intent(in): CHARACTER (LEN = *), INTENT(IN) :: inifile !!stores configuration information !local declarations: TYPE (IniList) :: iniDB INTEGER (KIND = short) :: k , i, j CHARACTER (LEN = 100) :: string REAL (KIND = float) :: scalar !-----------------------------------end of declarations------------------------ !open and read configuration file CALL IniOpen (inifile, iniDB) !read the number of aquifers IF ( KeyIsPresent ('aquifers', iniDB ) ) THEN basin % naquifers = IniReadInt ( 'aquifers', iniDB ) ELSE CALL Catch ('error', 'Groundwater', & 'aquifers not found in configuration file' ) END IF !allocate aquifers ALLOCATE ( basin % aquifer ( basin % naquifers ) ) !load parameters DO k = 1, basin % naquifers string = 'aquifer_' // ToString (k) IF (SectionIsPresent(TRIM(string), iniDB)) THEN !load domain and boundary condition id IF (SubSectionisPresent (subsection = 'boundary-condition-id', & section = TRIM(string), iniDB = iniDB) ) THEN CALL GridByIni ( ini = iniDB, & grid = basin % aquifer (k) % domainBC, & section = TRIM(string), & subsection = 'boundary-condition-id') ELSE CALL Catch ('error', 'Groundwater', & 'missing boundary-condition-id in configuration file: ', & argument = TRIM(string) ) END IF !load boundary condition value IF (SubSectionisPresent (subsection = 'boundary-condition-value', & section = TRIM(string), iniDB = iniDB) ) THEN IF (KeyIsPresent ('scalar', iniDB, TRIM(string), & 'boundary-condition-value' ) ) THEN scalar = IniReadReal ('scalar', iniDB, & TRIM(string), 'boundary-condition-value' ) CALL NewGrid (basin % aquifer (k) % valueBC, & basin % aquifer (k) % domainBC, scalar) ELSE CALL GridByIni ( ini = iniDB, & grid = basin % aquifer (k) % valueBC, & section = TRIM(string), & subsection = 'boundary-condition-value') END IF ELSE CALL Catch ('error', 'Groundwater', & 'missing boundary-condition-value in configuration file: ', & argument = TRIM(string) ) END IF !load initial head IF (SubSectionisPresent (subsection = 'initial-head', & section = TRIM(string), iniDB = iniDB) ) THEN IF (KeyIsPresent ('scalar', iniDB, TRIM(string), & 'initial-head' ) ) THEN scalar = IniReadReal ('scalar', iniDB, & TRIM(string), 'initial-head' ) CALL NewGrid (basin % aquifer (k) % head0, & basin % aquifer (k) % domainBC, scalar) ELSE CALL GridByIni ( ini = iniDB, & grid = basin % aquifer (k) % head0, & section = TRIM(string), & subsection = 'initial-head') END IF CALL NewGrid (basin % aquifer (k) % head1, & basin % aquifer (k) % domainBC, 0.) basin % aquifer (k) % head1 = basin % aquifer (k) % head0 !head boundary condition overlay DO i = 1, basin % aquifer (k) % domainBC % idim DO j = 1, basin % aquifer (k) % domainBC % jdim IF ( basin % aquifer (k) % domainBC % mat (i,j) == & BC_DIRICHLET ) THEN basin % aquifer (k) % head0 % mat (i,j) = & basin % aquifer (k) % valueBC % mat (i,j) basin % aquifer (k) % head1 % mat (i,j) = & basin % aquifer (k) % valueBC % mat (i,j) END IF END DO END DO ELSE CALL Catch ('error', 'Groundwater', & 'missing initial-condition-head in configuration file: ', & argument = TRIM(string) ) END IF !load top elevation IF (SubSectionisPresent (subsection = 'top-elevation', & section = TRIM(string), iniDB = iniDB) ) THEN IF (KeyIsPresent ('scalar', iniDB, TRIM(string), & 'top-elevation' ) ) THEN scalar = IniReadReal ('scalar', iniDB, & TRIM(string), 'top-elevation' ) CALL NewGrid (basin % aquifer (k) % top, & basin % aquifer (k) % domainBC, scalar) ELSE CALL GridByIni ( ini = iniDB, & grid = basin % aquifer (k) % top, & section = TRIM(string), & subsection = 'top-elevation') END IF ELSE CALL Catch ('error', 'Groundwater', & 'missing top-elevation in configuration file: ', & argument = TRIM(string) ) END IF !load bottom elevation IF (SubSectionisPresent (subsection = 'bottom-elevation', & section = TRIM(string), iniDB = iniDB) ) THEN IF (KeyIsPresent ('scalar', iniDB, TRIM(string), & 'bottom-elevation' ) ) THEN scalar = IniReadReal ('scalar', iniDB, & TRIM(string), 'bottom-elevation' ) CALL NewGrid (basin % aquifer (k) % bottom, & basin % aquifer (k) % domainBC, scalar) ELSE CALL GridByIni ( ini = iniDB, & grid = basin % aquifer (k) % bottom, & section = TRIM(string), & subsection = 'bottom-elevation') END IF ELSE CALL Catch ('error', 'Groundwater', & 'missing bottom-elevation in configuration file: ', & argument = TRIM(string) ) END IF !load hydraulic conductivity IF (SubSectionisPresent (subsection = 'hydraulic-conductivity', & section = TRIM(string), iniDB = iniDB) ) THEN IF (KeyIsPresent ('scalar', iniDB, TRIM(string), & 'hydraulic-conductivity' ) ) THEN scalar = IniReadReal ('scalar', iniDB, & TRIM(string), 'hydraulic-conductivity' ) CALL NewGrid (basin % aquifer (k) % Ks, & basin % aquifer (k) % domainBC, scalar) ELSE CALL GridByIni ( ini = iniDB, & grid = basin % aquifer (k) % Ks, & section = TRIM(string), & subsection = 'hydraulic-conductivity') END IF ELSE CALL Catch ('error', 'Groundwater', & 'missing hydraulic-conductivity in configuration file: ', & argument = TRIM(string) ) END IF !load specific yield IF (SubSectionisPresent (subsection = 'specific-yield', & section = TRIM(string), iniDB = iniDB) ) THEN IF (KeyIsPresent ('scalar', iniDB, TRIM(string), & 'specific-yield' ) ) THEN scalar = IniReadReal ('scalar', iniDB, & TRIM(string), 'specific-yield' ) CALL NewGrid (basin % aquifer (k) % sy, & basin % aquifer (k) % domainBC, scalar) ELSE CALL GridByIni ( ini = iniDB, & grid = basin % aquifer (k) % sy, & section = TRIM(string), & subsection = 'specific-yield') END IF ELSE CALL Catch ('error', 'Groundwater', & 'missing specific-yield in configuration file: ', & argument = TRIM(string) ) END IF ! aquitard hydraulic conductivity IF ( k < basin % naquifers ) THEN IF (SubSectionisPresent (subsection = 'aquitard-conductivity', & section = TRIM(string), iniDB = iniDB) ) THEN IF (KeyIsPresent ('scalar', iniDB, TRIM(string), & 'aquitard-conductivity' ) ) THEN scalar = IniReadReal ('scalar', iniDB, & TRIM(string), 'aquitard-conductivity' ) CALL NewGrid (basin % aquifer (k) % KsAquitard, & basin % aquifer (k) % domainBC, scalar) ELSE CALL GridByIni ( ini = iniDB, & grid = basin % aquifer (k) % KsAquitard, & section = TRIM(string), & subsection = 'aquitard-conductivity') END IF ELSE CALL Catch ('error', 'Groundwater', & 'missing aquitard-conductivity in configuration file: ', & argument = TRIM(string) ) END IF END IF ELSE CALL Catch ('error', 'Groundwater', & 'missing section in configuration file: ', & argument = TRIM(string) ) END IF END DO !allocate variables CALL NewGrid ( transmissivity, basin % aquifer (1) % domainBC, 0. ) CALL NewGrid ( fluxUpward, basin % aquifer (1) % domainBC, 0. ) CALL NewGrid ( fluxDownward, basin % aquifer (1) % domainBC, 0. ) CALL NewGrid ( vadoseDepth, basin % aquifer (1) % domainBC, 0. ) !check river-groundwater interaction IF ( SectionIsPresent ( 'river-groundwater', iniDB) ) THEN riverGroundwaterInteract = .TRUE. CALL GroundwaterRiverInit ( inifile ) ELSE riverGroundwaterInteract = .FALSE. END IF !destroy iniDB CALL IniClose (IniDB) END SUBROUTINE GroundwaterInit !============================================================================== !| Description: ! update groundwater head with Darcy law and mass conservation ! in two dimensions, with a macrocopic cellular automata approach !``` ! +-----+ ! | N | ! +-----+-----+-----+ ! | W | C | E | ! +-----+-----+-----+ ! | S | ! +-----+ !``` ! References: ! Ravazzani, G., Rametta, D., & Mancini, M.. (2011). Macroscopic cellular ! automata for groundwater modelling: a first approach. Environmental ! modelling & software, 26(5), 634–643. ! SUBROUTINE GroundwaterUpdate & ! ( time, hillslopeFlux, percolation, capflux ) IMPLICIT NONE !Arguments with intent(in): TYPE (DateTime), INTENT(IN) :: time TYPE (grid_real), INTENT(IN) :: hillslopeFlux !!flux from hillslope (m3/s) TYPE (grid_real), INTENT(IN) :: percolation !! depp infiltration from soil balance (m/s) TYPE (grid_real), INTENT(IN) :: capflux !! capillary flux from soil balance (m/s) !local declarations: INTEGER (KIND = short) :: i, j, k REAL (KIND = float) :: rechargeQ !!vertical recharge (m3/s) REAL (KIND = float) :: transNC !!mean transmissivity North-Centre (m2/s) REAL (KIND = float) :: transEC !!mean transmissivity East-Centre (m2/s) REAL (KIND = float) :: transSC !!mean transmissivity South-Centre (m2/s) REAL (KIND = float) :: transWC !!mean transmissivity West-Centre (m2/s) REAL (KIND = float) :: fluxNC !!flux North-Centre (m3/s) REAL (KIND = float) :: fluxEC !!flux East-Centre (m3/s) REAL (KIND = float) :: fluxSC !!flux South-Centre (m3/s) REAL (KIND = float) :: fluxWC !!flux West-Centre (m3/s) REAL (KIND = float) :: fluxNESW !! total flux from the 4 directions (m3/s) REAL (KIND = float) :: areaOfCell !! cell area (m2) REAL (KIND = float) :: rToG !!river to groundwater discharge (m3/s) REAL (KIND = float) :: gToR !!groundwater to river discharge (m3/s) !-------------------------------------end of declarations---------------------- !reset counter variables volumeNeumann = 0. volumeDirichlet = 0. volumeRecharge = 0. volumeChange = 0. volumeResidual = 0. !update boundary condition CALL GroundwaterParameterUpdate (time) !save head1 into head0 DO k = 1, basin % nAquifers basin % aquifer (k) % head0 = basin % aquifer (k) % head1 END DO !update flux from hillslope of first (surface) aquifer DO i = 1, basin % aquifer (1) % domainBC % idim DO j = 1, basin % aquifer (1) % domainBC % jdim IF ( basin % aquifer (1) % domainBC % mat (i,j) == & BC_NEUMANN ) THEN basin % aquifer (1) % valueBC % mat (i,j) = & hillslopeFlux % mat (i,j) END IF END DO END DO !update aquifers head DO k = 1, basin % nAquifers !compute local transmissivity DO i = 1, basin % aquifer (k) % domainBC % idim DO j = 1, basin % aquifer (k) % domainBC % jdim IF ( basin % aquifer (k) % domainBC % mat(i,j) /= & basin % aquifer (k) % domainBC % nodata ) THEN !transmissivity: for confined aquifer = ks * aquifer depth transmissivity % mat (i,j) = MIN ( & basin % aquifer (k) % Ks % mat (i,j) * & ( basin % aquifer (k) % head0 % mat (i,j) - & basin % aquifer (k) % bottom % mat (i,j) ) , & basin % aquifer (k) % Ks % mat (i,j) * & ( basin % aquifer (k) % top % mat (i,j) - & basin % aquifer (k) % bottom % mat (i,j) ) & ) IF ( transmissivity % mat (i,j) <= 0. ) THEN transmissivity % mat (i,j) = 0.01 END IF END IF END DO END DO !update head1 DO i = 1, basin % aquifer (k) % domainBC % idim DO j = 1, basin % aquifer (k) % domainBC % jdim IF ( basin % aquifer (k) % domainBC % mat(i,j) == & ACTIVE_CELL ) THEN !harmonic mean transmissivity transNC = 2.0 * transmissivity % mat(i-1,j) * & transmissivity % mat(i,j) / & ( transmissivity %mat(i-1,j) + & transmissivity %mat(i,j) ) transSC = 2.0 * transmissivity % mat(i+1,j) * & transmissivity % mat(i,j) / & ( transmissivity % mat(i+1,j) + & transmissivity % mat(i,j) ) transEC = 2.0 * transmissivity % mat(i,j+1) * & transmissivity % mat(i,j) / & ( transmissivity % mat(i,j+1) + & transmissivity % mat(i,j) ) transWC = 2.0 * transmissivity % mat(i,j-1) * & transmissivity % mat(i,j) / & ( transmissivity % mat(i,j-1) + & transmissivity % mat(i,j) ) !flux from North IF ( basin % aquifer (k) % domainBC % mat(i-1,j) == & BC_NEUMANN ) THEN fluxNC = basin % aquifer (k) % valueBC % mat(i-1,j) volumeNeumann = volumeNeumann + fluxNC * dtGroundwater ELSE fluxNC = transNC * & ( basin % aquifer (k) % head0 % mat (i-1,j) - & basin % aquifer (k) % head0 % mat (i,j) ) IF ( basin % aquifer (k) % domainBC % mat(i-1,j) == & BC_DIRICHLET ) THEN volumeDirichlet = volumeDirichlet + & fluxNC * dtGroundwater END IF END IF !flux from East IF ( basin % aquifer (k) % domainBC % mat(i,j+1) == & BC_NEUMANN ) THEN fluxEC = basin % aquifer (k) % valueBC % mat(i,j+1) volumeNeumann = volumeNeumann + fluxEC * dtGroundwater ELSE fluxEC = transEC * & ( basin % aquifer (k) % head0 % mat (i,j+1) - & basin % aquifer (k) % head0 % mat (i,j) ) IF ( basin % aquifer (k) % domainBC % mat(i,j+1) == & BC_DIRICHLET ) THEN volumeDirichlet = volumeDirichlet + & fluxEC * dtGroundwater END IF END IF !flux from South IF ( basin % aquifer (k) % domainBC % mat(i+1,j) == & BC_NEUMANN ) THEN fluxSC = basin % aquifer (k) % valueBC % mat(i+1,j) volumeNeumann = volumeNeumann + fluxSC * dtGroundwater ELSE fluxSC = transSC * & ( basin % aquifer (k) % head0 % mat (i+1,j) - & basin % aquifer (k) % head0 % mat (i,j) ) IF ( basin % aquifer (k) % domainBC % mat(i+1,j) == & BC_DIRICHLET ) THEN volumeDirichlet = volumeDirichlet + & fluxSC * dtGroundwater END IF END IF !flux from West IF ( basin % aquifer (k) % domainBC % mat(i,j-1) == & BC_NEUMANN ) THEN fluxWC = basin % aquifer (k) % valueBC % mat(i,j-1) volumeNeumann = volumeNeumann + fluxWC * dtGroundwater ELSE fluxWC = transWC * & ( basin % aquifer (k) % head0 % mat (i,j-1) - & basin % aquifer (k) % head0 % mat (i,j) ) IF ( basin % aquifer (k) % domainBC % mat(i,j-1) == & BC_DIRICHLET ) THEN volumeDirichlet = volumeDirichlet + & fluxWC * dtGroundwater END IF END IF !total flux fluxNESW = fluxNC + fluxEC + fluxSC + fluxWC !area of the cell areaOfCell = CellArea (transmissivity, i, j) !vertical recharge IF ( ALLOCATED (percolation % mat ) ) THEN IF ( k == 1 .AND. percolation % mat (i,j) /= & percolation % nodata ) THEN rechargeQ = ( percolation % mat (i,j) - & capflux % mat (i,j) ) * areaOfCell volumeRecharge = volumeRecharge + & rechargeQ * dtGroundwater ELSE rechargeQ = 0. END IF ELSE rechargeQ = 0. END IF !upward flux from lower aquifer IF (k /= basin % nAquifers ) THEN fluxUpward % mat(i,j) = & basin % aquifer (k) % ksAquitard % mat(i,j) * & ( ( basin % aquifer (k+1) % head0 % mat(i,j) - & basin % aquifer (k) % head0 % mat(i,j) ) / & ( basin % aquifer (k) % bottom % mat(i,j) - & basin % aquifer (k+1) % top % mat(i,j) ) ) ELSE fluxUpward % mat (i,j) = 0.0 END IF !update head1 IF ( k == 1) THEN fluxDownward % mat (i,j) = 0. IF ( riverGroundwaterInteract ) THEN rToG = riverToGroundwater % mat (i,j) gToR = - groundwaterToRiver % mat (i,j) ELSE rToG = 0. gToR = 0. END IF ELSE rToG = 0. gToR = 0. END IF basin % aquifer (k) % head1 % mat(i,j) = & basin % aquifer (k) % head0 % mat(i,j) + & ( fluxNESW + rechargeQ + rToG + gToR ) * & dtGroundwater / areaOfCell / & basin % aquifer (k) % sy % mat(i,j) + & ( fluxUpward % mat(i,j) + fluxDownward % mat(i,j) ) * & dtGroundwater / basin % aquifer (k) % sy % mat(i,j) IF ( ISNAN ( basin % aquifer (k) % head1 % mat(i,j) ) ) THEN basin % aquifer (k) % head1 % mat(i,j) = & basin % aquifer (k) % head0 % mat(i,j) volumeChange = 0. ELSE !volume change volumeChange = volumeChange + & ( basin % aquifer (k) % head1 % mat(i,j) - & basin % aquifer (k) % head0 % mat(i,j) ) * & basin % aquifer (k) % sy % mat(i,j) * areaOfCell END IF !swap fluxUpward and fluxDownward fluxDownward % mat (i,j) = - fluxUpward % mat (i,j) !update vadose zone depth IF ( k == 1) THEN vadoseDepth % mat (i,j) = & basin % aquifer (k) % top % mat(i,j) - & basin % aquifer (k) % head1 % mat(i,j) END IF END IF END DO END DO END DO !compute balance error volumeResidual = volumeRecharge + volumeDirichlet + & volumeNeumann + volumeRiverToGroundwater - & volumeGroundwaterToRiver - volumeChange !write point data IF ( time == timePointExport ) THEN CALL GroundwaterPointExport ( time ) END IF RETURN END SUBROUTINE GroundwaterUpdate !============================================================================== !| Description: ! update boundary condition map that change in time SUBROUTINE GroundwaterParameterUpdate & ! (time) IMPLICIT NONE !Arguments with intent(in): TYPE (DateTime), INTENT (IN) :: time !local declarations: CHARACTER (LEN = 300) :: filename CHARACTER (LEN = 300) :: varname INTEGER (KIND = short) :: k, i, j !------------------------------end of declarations----------------------------- !boundary condition DO k = 1, basin % naquifers IF ( time == basin % aquifer (k) % valueBC % next_time ) THEN !destroy current grid filename = basin % aquifer (k) % valueBC % file_name varname = basin % aquifer (k) % valueBC % var_name CALL GridDestroy (basin % aquifer (k) % valueBC ) !read new grid in netcdf file CALL NewGrid (basin % aquifer (k) % valueBC, TRIM(filename), NET_CDF, & variable = TRIM(varname), time = time) !head boundary condition overlay DO i = 1, basin % aquifer (k) % domainBC % idim DO j = 1, basin % aquifer (k) % domainBC % jdim IF ( basin % aquifer (k) % domainBC % mat (i,j) == & BC_DIRICHLET ) THEN basin % aquifer (k) % head0 % mat (i,j) = & basin % aquifer (k) % valueBC % mat (i,j) basin % aquifer (k) % head1 % mat (i,j) = & basin % aquifer (k) % valueBC % mat (i,j) END IF END DO END DO END IF END DO RETURN END SUBROUTINE GroundwaterParameterUpdate !============================================================================== !| Description: ! Initialize export of point site data SUBROUTINE GroundwaterPointInit & ! ( pointfile, path_out, time ) IMPLICIT NONE !Arguments with intent (in): CHARACTER (LEN = *), INTENT(IN) :: pointfile !!file containing coordinate of points CHARACTER (LEN = *), INTENT(IN) :: path_out !!path of output folder TYPE (DateTime), INTENT(IN) :: time !!start time !local declarations INTEGER (KIND = short) :: err_io INTEGER (KIND = short) :: fileunit INTEGER (KIND = short) :: k !-------------------------end of declarations---------------------------------- timePointExport = time fileunit = GetUnit () OPEN ( unit = fileunit, file = pointfile(1:LEN_TRIM(pointfile)), & status='OLD', iostat = err_io ) IF ( err_io /= 0 ) THEN !file does not exist CALL Catch ('error', 'Groundwater', 'out point file does not exist') END IF !Read metadata CALL ReadMetadata (fileunit, sites) !check dt IF (.NOT. ( MOD ( sites % timeIncrement, dtGroundwater ) == 0 ) ) THEN CALL Catch ('error', 'Groundwater', 'dt out sites must be multiple of dtGroundwater ') END IF CLOSE ( fileunit ) sites % description = 'groundwater head data exported from FEST' sites % unit = 'm' sites % offsetZ = 0. !allocate file unit ALLOCATE ( fileUnitPointGW ( basin % nAquifers ) ) !open and initialize files DO k = 1, basin % nAquifers fileUnitPointGW (k) = GetUnit () OPEN ( unit = fileUnitPointGW (k), & file = TRIM(path_out) // 'point_aquifer_' // TRIM(ToString (k)) // '.fts' ) CALL WriteMetadata ( network = sites, fileunit = fileUnitPointGW (k) ) CALL WriteData (sites, fileUnitPointGW (k), .TRUE.) END DO RETURN END SUBROUTINE GroundwaterPointInit !============================================================================== !| Description: ! Export of point site data SUBROUTINE GroundwaterPointExport & ! ( time ) IMPLICIT NONE !Arguments with intent(in): TYPE (DateTime), INTENT (IN) :: time !local declarations: INTEGER (KIND = short) :: k !-------------------------end of declarations---------------------------------- !set current time sites % time = time DO k = 1, basin % nAquifers !populate data CALL AssignDataFromGrid ( basin % aquifer (k) % head1, sites ) !write data CALL WriteData ( sites, fileUnitPointGW (k) ) END DO timePointExport = timePointExport + sites % timeIncrement RETURN END SUBROUTINE GroundwaterPointExport !============================================================================== !| Description: ! Initialize files for exporting output results SUBROUTINE GroundwaterOutputInit & ! ( path_out ) IMPLICIT NONE !Arguments with intent (in): CHARACTER (LEN = *), INTENT(IN) :: path_out !!path of output folder !local declarations REAL (KIND = float) :: area !! (km2) INTEGER (KIND = short) :: i, j INTEGER (KIND = short) :: nvars !----------------------------------end of declarations------------------------- !compute groundwater extent area area = 0. DO i = 1, basin % aquifer (1) % domainBC % idim DO j = 1, basin % aquifer (1) % domainBC % jdim IF (basin % aquifer (1) % domainBC % mat (i,j) == ACTIVE_CELL ) THEN area = area + CellArea ( basin % aquifer (1) % domainBC, i, j ) END IF END DO END DO area = area / 1000000. !conversion m2 -> km2 !number of variables to write in output file nvars = basin % nAquifers + 5 IF ( riverGroundwaterInteract ) THEN nvars = nvars + 2 END IF !open and initialize file fileUnitOutGW = GetUnit () OPEN ( unit = fileUnitOutGW, file = TRIM(path_out) // 'aquifer.out' ) WRITE (fileUnitOutGW,'(a)') 'Output variables of groundwater simulation' WRITE (fileUnitOutGW,fmt = '(a, F12.2)') 'extent area (km2): ', area WRITE (fileUnitOutGW,fmt = '(a, I3)') 'number of variables: ', nvars WRITE (fileUnitOutGW,*) WRITE (fileUnitOutGW,*) WRITE (fileUnitOutGW,'(a)') 'data' WRITE (fileUnitOutGW,'(a)', advance='no') 'DateTime ' DO i = 1, basin % nAquifers WRITE (fileUnitOutGW,'(a)', advance='no') 'head_aquifer_' // & TRIM(ToString(i)) // '_m' END DO IF ( riverGroundwaterInteract ) THEN WRITE (fileUnitOutGW,'(a)') ' recharge_m3 volume_neumann_m3 & volume_dirichlet_m3 volume_change_m3 residual_m3 & river_to_groundwater_m3 groundwater_to_river_m3 ' ELSE WRITE (fileUnitOutGW,'(a)') ' recharge_m3 volume_neumann_m3 & volume_dirichlet_m3 volume_change_m3 residual_m3 ' END IF RETURN END SUBROUTINE GroundwaterOutputInit !============================================================================== !! Description: !! Write results on output file SUBROUTINE GroundwaterOutput & ! ( time ) IMPLICIT NONE !Arguments with intent (in): TYPE (DateTime), INTENT(IN) :: time !! current time !local variables: INTEGER (KIND = short) :: k REAL (KIND = float) :: meanHead !----------------------------end of declarations------------------------------- timeString = time WRITE (fileUnitOutGW,'(a)',advance='no') timeString DO k = 1, basin % nAquifers meanHead = GetMean ( basin % aquifer (k) % head1, & maskInteger = basin % aquifer (k) % domainBC ) WRITE (fileUnitOutGW,'(6x,E12.5)',advance='no') meanHead END DO IF ( riverGroundwaterInteract ) THEN WRITE (fileUnitOutGW,'(7(5x,E12.5))') volumeRecharge, volumeNeumann, & volumeDirichlet, volumeChange, volumeResidual, & volumeRiverToGroundwater, volumeGroundwaterToRiver ELSE WRITE (fileUnitOutGW,'(5(5x,E12.5))') volumeRecharge, volumeNeumann, & volumeDirichlet, volumeChange, volumeResidual END IF RETURN END SUBROUTINE GroundwaterOutput !============================================================================== !| Description: ! Configure river-groundwater interaction SUBROUTINE GroundwaterRiverInit & ! ( inifile ) IMPLICIT NONE !Arguments with intent (in): CHARACTER (LEN = *), INTENT(IN) :: inifile !! configuration file !local variables: TYPE (IniList) :: iniDB INTEGER (KIND = short) :: i,j !----------------------------end of declarations------------------------------- !open and read configuration file CALL IniOpen ( inifile, iniDB ) !load river id IF (SubSectionisPresent (subsection = 'river-id', & section = 'river-groundwater', iniDB = iniDB) ) THEN CALL GridByIni ( ini = iniDB, & grid = riverGroundwaterID, & section = 'river-groundwater', & subsection = 'river-id') ELSE CALL Catch ('error', 'Groundwater', & 'missing river-id in configuration file' ) END IF !load river dem IF (SubSectionisPresent (subsection = 'river-dem', & section = 'river-groundwater', iniDB = iniDB) ) THEN CALL GridByIni ( ini = iniDB, & grid = riverDem, & section = 'river-groundwater', & subsection = 'river-dem') ELSE CALL Catch ('error', 'Groundwater', & 'missing river-dem in configuration file' ) END IF !exchange parameters CALL TableNew ( file = inifile, tab = riverGroundwaterParameters, & id = 'river-groundwater') !allocate exchange flux maps CALL NewGrid ( riverToGroundwater, riverGroundwaterID, 0. ) CALL NewGrid ( groundwaterToRiver, riverGroundwaterID, 0. ) !allocate streambed parameter maps CALL NewGrid ( streambedThickness, riverGroundwaterID, 0. ) CALL NewGrid ( streambedConductivity, riverGroundwaterID, 0. ) !populate streambed parameter maps DO i = 1, riverGroundwaterID % idim DO j = 1, riverGroundwaterID % jdim IF ( riverGroundwaterID % mat (i,j) /= & riverGroundwaterID % nodata ) THEN CALL TableGetValue ( & valueIn = REAL(riverGroundwaterID % mat (i,j)),& tab = riverGroundwaterParameters, & keyIn = 'id', keyOut ='streambed-conductivity', & match = 'exact', valueOut = streambedConductivity % mat (i,j) ) CALL TableGetValue ( & valueIn = REAL(riverGroundwaterID % mat (i,j)),& tab = riverGroundwaterParameters, & keyIn = 'id', keyOut ='streambed-thickness', & match = 'exact', valueOut = streambedThickness % mat (i,j) ) END IF END DO END DO !free db CALL IniClose ( iniDB ) RETURN END SUBROUTINE GroundwaterRiverInit !============================================================================== !| Description: ! Update river-groundwater exchange fluxes SUBROUTINE GroundwaterRiverUpdate & ! ( waterDepth, topWidth ) IMPLICIT NONE !Arguments with intent (in): TYPE (grid_real), INTENT(IN) :: waterDepth !!river water depth (m) TYPE (grid_real), INTENT(IN) :: topWidth !!river top width (m) !local variables: INTEGER (KIND = short) :: i, j REAL (KIND = float) :: riverWSE !river water surface elevation (m asl) !TYPE (grid_real) :: waterTable !!aquifer water table (m) REAL (KIND = float) :: waterTable REAL (KIND = float) :: area !-----------------------------end of declarations------------------------------ volumeRiverToGroundwater = 0. volumeGroundwaterToRiver = 0. !waterTable = basin % aquifer (1) % head1 DO i = 1, riverGroundwaterID % idim DO j = 1, riverGroundwaterID % jdim IF ( riverGroundwaterID % mat (i,j) /= & riverGroundwaterID % nodata ) THEN waterTable = basin % aquifer (1) % head1 % mat (i,j) riverWSE = waterDepth % mat (i,j) + riverDem % mat (i,j) area = CellArea (riverGroundwaterID, i, j) IF ( waterTable > riverWSE ) THEN groundwaterToRiver % mat (i,j) = & ( waterTable - riverWSE ) * & !head difference streambedConductivity % mat (i,j) / & !conductivity streambedThickness % mat (i,j) * & !thickness topWidth % mat (i,j) * & !river width area ** 0.5 !cell size riverToGroundwater % mat (i,j) = 0. ELSE IF ( waterTable < riverWSE .AND. & waterTable > riverDem % mat (i,j) ) THEN ! when waterdepth > 10 cm, compute river discharge toward groundwater IF ( waterDepth % mat (i,j) > 0.10 ) THEN riverToGroundwater % mat(i,j) = & ( riverWSE - waterTable ) * & !head difference streambedConductivity % mat (i,j) / & !conductivity streambedThickness % mat (i,j) * & !thickness topWidth % mat (i,j) * & !river width area ** 0.5 !cell size groundwaterToRiver % mat (i,j) = 0. ELSE riverToGroundwater % mat(i,j) = 0. END IF ELSE IF ( waterTable < riverDem % mat (i,j) ) THEN ! when waterdepth > 10 cm, compute river discharge toward groundwater IF ( waterDepth % mat (i,j) > 0.10 ) THEN riverToGroundwater % mat(i,j) = & waterDepth % mat (i,j) * & !head streambedConductivity % mat (i,j) / & !conductivity streambedThickness % mat (i,j) * & !thickness topWidth % mat (i,j) * & !river width area ** 0.5 !cell size groundwaterToRiver % mat (i,j) = 0. ELSE riverToGroundwater % mat(i,j) = 0. END IF END IF volumeRiverToGroundwater = volumeRiverToGroundwater + & riverToGroundwater % mat (i,j) * & dtGroundwater volumeGroundwaterToRiver = volumeGroundwaterToRiver + & groundwaterToRiver % mat (i,j) * & dtGroundwater END IF END DO END DO RETURN END SUBROUTINE GroundwaterRiverUpdate END MODULE Groundwater